const crypto = require('crypto');

/**
 * 基于PKCS7算法的加解密接口
 *
 */
const PKCS7Encoder = {
    /**
     * 删除解密后明文的补位字符
     *
     * @param {String} text 解密后的明文
     */
    decode(text) {
        var pad = text[text.length - 1];

        if (pad < 1 || pad > 32) {
            pad = 0;
        }

        return text.slice(0, text.length - pad);
    },

    /**
     * 对需要加密的明文进行填充补位
     *
     * @param {String} text 需要进行填充补位操作的明文
     */
    encode(text) {
        var blockSize = 32;
        var textLength = text.length;
        //计算需要填充的位数
        var amountToPad = blockSize - (textLength % blockSize);

        var result = new Buffer(amountToPad);
        result.fill(amountToPad);

        return Buffer.concat([text, result]);
    }
}

class wxCrypto {
    constructor({
        token,
        encodingAESKey,
        id
    }) {
        this.token = token;
        this.id = id;
        this.key = new Buffer(encodingAESKey + '=', 'base64');
        this.iv = this.key.slice(0, 16);
    }

    /**
     * 获取签名
     *
     * @param {String} timestamp    时间戳
     * @param {String} nonce        随机数
     * @param {String} encrypt      加密后的文本
     */
    getSignature(timestamp, nonce, encrypt) {
        let shasum = crypto.createHash('sha1');
        let arr = [this.token, timestamp, nonce, encrypt].sort();
        shasum.update(arr.join(''));

        return shasum.digest('hex');
    };

    checkSignature(timestamp, nonce, token, signature) {
        token = token == undefined ? this.token : token;
        let shasum = crypto.createHash('sha1');
        let arr = [nonce, timestamp, token].sort();
        shasum.update(arr.join(''));
        return shasum.digest('hex') === signature;
    }
    /**
     * 对密文进行解密
     *
     * @param {String} text 待解密的密文
     */
    decrypt(text) {
        // 创建解密对象，AES采用CBC模式，数据采用PKCS#7填充；IV初始向量大小为16字节，取AESKey前16字节
        let decipher = crypto.createDecipheriv('aes-256-cbc', this.key, this.iv);
        decipher.setAutoPadding(false);
        let deciphered = Buffer.concat([decipher.update(text, 'base64'), decipher.final()]);

        deciphered = PKCS7Encoder.decode(deciphered);
        // 算法：AES_Encrypt[random(16B) + msg_len(4B) + msg + $CorpID]
        // 去除16位随机数
        let content = deciphered.slice(16);
        let length = content.slice(0, 4).readUInt32BE(0);

        return {
            message: content.slice(4, length + 4).toString(),
            id: content.slice(length + 4).toString()
        };
    };

    /**
     * 对明文进行加密
     *
     * @param {String} text 待加密的明文
     */
    encrypt(text) {
        // 算法：AES_Encrypt[random(16B) + msg_len(4B) + msg + $CorpID]
        // 获取16B的随机字符串
        let randomString = crypto.pseudoRandomBytes(16);

        let msg = new Buffer(text);

        // 获取4B的内容长度的网络字节序
        let msgLength = new Buffer(4);
        msgLength.writeUInt32BE(msg.length, 0);

        let id = new Buffer(this.id);

        let bufMsg = Buffer.concat([randomString, msgLength, msg, id]);

        // 对明文进行补位操作
        let encoded = PKCS7Encoder.encode(bufMsg);

        // 创建加密对象，AES采用CBC模式，数据采用PKCS#7填充；IV初始向量大小为16字节，取AESKey前16字节
        let cipher = crypto.createCipheriv('aes-256-cbc', this.key, this.iv);
        cipher.setAutoPadding(false);

        let cipheredMsg = Buffer.concat([cipher.update(encoded), cipher.final()]);

        // 返回加密数据的base64编码
        return cipheredMsg.toString('base64');
    };

}

module.exports = wxCrypto;